/*
 * Copyright 2021-2024, Haiku Inc.
 * Distributed under the terms of the MIT License.
 *
 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
 * Distributed under the terms of the NewOS License.
 */

#include <arch/debug.h>

#include <stdio.h>
#include <stdlib.h>

#include <ByteOrder.h>
#include <TypeConstants.h>

#include <cpu.h>
#include <debug.h>
#include <debug_heap.h>
#include <elf.h>
#include <kernel.h>
#include <kimage.h>
#include <thread.h>
#include <vm/vm.h>
#include <vm/vm_types.h>
#include <vm/VMAddressSpace.h>
#include <vm/VMArea.h>

#include "LoongArchVMTranslationMap.h"

static bool
iframe_is_user(iframe *frame)
{
    // TODO: Implement for LoongArch64
    // Check if the frame is from user mode based on status register
    return (frame->csr_prmd & 0x3) == 0x3; // PRMD.PPLV = 3 for user mode
}

static iframe *
get_user_iframe()
{
    Thread *thread = thread_get_current_thread();
    return thread->arch_info.userFrame;
}

static phys_addr_t
get_thread_page_directory(Thread *thread)
{
    auto map = (LoongArchVMTranslationMap *)thread->team->address_space->TranslationMap();
    return map->PageDirectory();
}

struct stack_frame
{
    addr_t previous;
    addr_t return_address;
};

#define NUM_PREVIOUS_LOCATIONS 32

static bool
already_visited(addr_t *visited, int32 *_last, int32 *_num, addr_t fp)
{
    int32 last = *_last;
    int32 num = *_num;
    int32 i;

    for (i = 0; i < num; i++)
    {
        if (visited[(NUM_PREVIOUS_LOCATIONS + last - i) % NUM_PREVIOUS_LOCATIONS] == fp)
            return true;
    }

    *_last = last = (last + 1) % NUM_PREVIOUS_LOCATIONS;
    visited[last] = fp;

    if (num < NUM_PREVIOUS_LOCATIONS)
        *_num = num + 1;

    return false;
}

status_t
arch_debug_get_instruction_pointer(addr_t *_ip, iframe *frame)
{
    if (frame != NULL)
    {
        *_ip = frame->csr_era;
        return B_OK;
    }

    // If we don't have a valid iframe, we need to read the current PC
    // This is a simplified implementation
    *_ip = (addr_t)arch_debug_get_caller();
    return B_OK;
}

status_t
arch_debug_get_stack_frame(addr_t *_fp, iframe *frame)
{
    if (frame != NULL)
    {
        *_fp = frame->reg[22]; // s0/fp register in LoongArch64
        return B_OK;
    }

    // If we don't have a valid iframe, we need to read the current FP
    // This is a simplified implementation
    addr_t fp;
    asm volatile("move %0, $s0" : "=r"(fp));
    *_fp = fp;
    return B_OK;
}

status_t
arch_debug_get_stack_trace(addr_t *returnAddresses, size_t bufferSize,
                           size_t skipFrames, addr_t stackFrameAddress, iframe *frame)
{
    // This is a simplified implementation
    // A complete implementation would need to handle unwinding the stack properly
    return B_NOT_SUPPORTED;
}

status_t
arch_debug_get_caller(addr_t *_return_address)
{
    // This is a simplified implementation
    // A complete implementation would need to get the return address from the link register
    return B_NOT_SUPPORTED;
}

status_t
arch_debug_get_stack_trace_from_return_address(addr_t returnAddress,
                                               addr_t *returnAddresses, size_t bufferSize, size_t skipFrames)
{
    // This is a simplified implementation
    return B_NOT_SUPPORTED;
}

status_t
arch_debug_init(kernel_args *args)
{
    return B_OK;
}

void arch_debug_unset_current_thread()
{
}

void arch_debug_set_current_thread(Thread *thread)
{
}

bool arch_debug_contains_call(Thread *thread, const char *symbol,
                              addr_t start, addr_t end)
{
    // This is a simplified implementation
    return false;
}

void *
arch_debug_call_with_fault_handler(cpu_ent *cpu, jmp_buf jumpBuffer,
                                   void (*function)(void *), void *parameter)
{
    // This is a simplified implementation
    return NULL;
}

bool arch_is_debug_variable_defined(const char *variableName)
{
    return false;
}

status_t
arch_set_debug_variable(const char *variableName, uint64 value)
{
    return B_NOT_SUPPORTED;
}

status_t
arch_get_debug_variable(const char *variableName, uint64 *value)
{
    return B_NOT_SUPPORTED;
}

bool arch_debug_blue_screen_fault_handler(void *address, bool isWrite)
{
    return false;
}

bool arch_debug_screen_fault_handler(void *address, bool isWrite)
{
    return false;
}

void arch_debug_stack_trace_entry_hook(addr_t address, addr_t returnAddress,
                                       addr_t stackFrameAddress, addr_t stackAddress)
{
}